use std::path::Path;

use axum::{
    extract::Request,
    http::{header, HeaderValue, Method, StatusCode},
    response::{IntoResponse, Response},
    Router,
};
use tokio::{fs, net::TcpListener};

use crate::{config, error::AppErr};

mod model;
mod req;
mod resp;
mod msg;
mod api;

pub use model::Download;

pub async fn run() -> Result<(), AppErr> {
    log::info!("app run: {}", config::WEB_ADDR);
    msg::init_once();
    let ln = TcpListener::bind(config::WEB_ADDR).await?;
    let router = Router::new()
        .nest("/api", api::register())
        .nest("/msg", msg::register())
        .fallback(on_fallback);
    axum::serve(ln, router).await?;
    Ok(())
}

async fn on_fallback(req: Request) -> Response {
    if req.method() != Method::GET {
        return StatusCode::METHOD_NOT_ALLOWED.into_response();
    }

    let path = req.uri().path();

    let path = format!("{}{}", config::WEB_DIR, path);

    let path = if Path::new(&path).extension().is_some() {
        &path
    } else {
        config::HTML_PATH
    };

    let data = match fs::read(&path).await {
        Ok(v) => v,
        Err(_) => {
            return StatusCode::NOT_FOUND.into_response()
        }
    };
    let mut resp = data.into_response();
    let ct = mime_guess::from_path(path).first_or_octet_stream();
    *resp.status_mut() = StatusCode::OK;
    resp.headers_mut().insert(
        header::CONTENT_TYPE,
        HeaderValue::from_str(ct.essence_str()).unwrap_or(resp::CONTENT_TYPE_BINARY),
    );

    resp
}
